using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Slf.Resolvers;
using Slf;
using Slf.Factories;

namespace SLF.Test.Resolver
{
  [TestClass]
  public class NamedFactoryResolverTest
  {
    [TestMethod]
    public void Test()
    {
      var resolver = new NamedFactoryResolver();
      var cf = new ConsoleLoggerFactory();
      resolver.RegisterFactory("foo", cf);
      LoggerService.FactoryResolver = resolver;

      Assert.IsInstanceOfType(LoggerService.GetLogger("foo.xxx"), typeof(ConsoleLogger));
    }

    [TestMethod]
    public void NoFactoriesAdded_GetFactory_ReturnsNullLoggerFactory()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      Assert.IsInstanceOfType(resolver.GetFactory("foo"), typeof(NullLoggerFactory));
    }

    [TestMethod]
    public void AddFactory_AddAFactory_FactoryIsAdded()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add a factory
      ILoggerFactory factory = new DummyFactory();
      resolver.RegisterFactory("FactoryOne", factory);

      // validate
      Assert.AreEqual(1, resolver._factoryCache.Count);
      Assert.AreEqual(factory, resolver._factoryCache[0].Factory);
      Assert.AreEqual("FactoryOne", resolver._factoryCache[0].Name);
    }

    [TestMethod]
    public void AddFactory_MultipleFactories_FactoriesSortedByName()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add factories
      resolver.RegisterFactory("Short", new DummyFactory());
      resolver.RegisterFactory("AVeryLong.Long.Name", new DummyFactory());
      resolver.RegisterFactory("Tiny", new DummyFactory());
      resolver.RegisterFactory("Medium.Length", new DummyFactory());

      // validate
      Assert.AreEqual(4, resolver._factoryCache.Count);
      Assert.AreEqual("AVeryLong.Long.Name", resolver._factoryCache[0].Name);
      Assert.AreEqual("Medium.Length", resolver._factoryCache[1].Name);
      Assert.AreEqual("Short", resolver._factoryCache[2].Name);
      Assert.AreEqual("Tiny", resolver._factoryCache[3].Name);
    }
        

    [TestMethod]
    public void GetFactoryByName_NoFactoriesSupplied_NullFactoryReturned()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      ILoggerFactory factory = resolver.GetFactory("wibble");
      Assert.AreEqual("Slf.Factories.NullLoggerFactory", factory.GetType().ToString());
    }



    [TestMethod]
    public void GetFactoryByName_MultipleFactories_ResolvedBySpecificity()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      DummyFactory fact1 = new DummyFactory();
      DummyFactory fact2 = new DummyFactory();
      DummyFactory fact3 = new DummyFactory();
      DummyFactory fact4 = new DummyFactory();

      // add factories
      DummyFactory leastSpecific = new DummyFactory();
      resolver.RegisterFactory("Client.DataLayer", fact1);
      resolver.RegisterFactory("Client.DataLayer.Audit", fact2);
      resolver.RegisterFactory("Client", fact3);
      resolver.RegisterFactory("Server", fact4);

      // verify - exact matches
      Assert.AreEqual(fact3, resolver.GetFactory("Client"));
      Assert.AreEqual(fact4, resolver.GetFactory("Server"));
      Assert.AreEqual(fact2, resolver.GetFactory("Client.DataLayer.Audit"));
      Assert.AreEqual(fact1, resolver.GetFactory("Client.DataLayer"));

      // verify - matches which rely on specificity
      Assert.AreEqual(fact3, resolver.GetFactory("Client.ServiceLayer"));
      Assert.AreEqual(fact2, resolver.GetFactory("Client.DataLayer.Audit.Impl"));
      Assert.AreEqual(fact1, resolver.GetFactory("Client.DataLayer.Messaging"));
      Assert.AreEqual(fact4, resolver.GetFactory("Server.Config"));
    }

    [TestMethod]
    public void GetFactoryByName_MultipleFactories_ResolvedCaseInsensitive()
    {
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      DummyFactory fact1 = new DummyFactory();
      DummyFactory fact2 = new DummyFactory();

      // add factories
      DummyFactory leastSpecific = new DummyFactory();
      resolver.RegisterFactory("Client.DataLayer", fact1);
      resolver.RegisterFactory("Client.DataLayer.Audit", fact2);

      // verify - exact matches
      Assert.AreEqual(fact2, resolver.GetFactory("CLIENT.DATALAYER.AUDIT"));
      Assert.AreEqual(fact1, resolver.GetFactory("Client.dataLayer"));
    }


    [TestMethod]
    public void Resolver_Should_Not_Return_Default_Factory_In_Case_Of_More_Specific_Logger()
    {
      //make sure we don't return a named factory that just doesn't match
      //e.g. returning factory "foo" if we're asking for factory "bar"
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add factories
      resolver.RegisterFactory("foo.bar", new DummyFactory("foo.bar"));
      resolver.RegisterFactory("bar.foo", new DummyFactory("bar.foo"));
      resolver.RegisterFactory("", new DummyFactory("DEFAULT"));
      resolver.RegisterFactory("foo", new DummyFactory("foo"));

      var factory = (DummyFactory)resolver.GetFactory("foo.x");
      Assert.IsNotNull(factory);
      Assert.AreEqual("foo", factory.Name);
    }


   
    [TestMethod]
    public void Resolver_Should_Return_Default_Factory_In_Case_Of_Unknown_Logger_Name()
    {
      //make sure we don't return a named factory that just doesn't match
      //e.g. returning factory "foo" if we're asking for factory "bar"
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add factories
      resolver.RegisterFactory("foo.bar", new DummyFactory("foo.bar"));
      resolver.RegisterFactory("bar.foo", new DummyFactory("bar.foo"));
      resolver.RegisterFactory("", new DummyFactory("DEFAULT"));
      resolver.RegisterFactory("bar", new DummyFactory("bar"));

      var factory = (DummyFactory)resolver.GetFactory("test");
      Assert.IsNotNull(factory);
      Assert.AreEqual("DEFAULT", factory.Name);
    }


    [TestMethod]
    public void Resolver_Should_Return_NullLoggerFactory_For_Unknown_Logger_Name_If_No_Default_Factory_Was_Is_Specified()
    {
      //make sure we don't return a named factory that just doesn't match
      //e.g. returning factory "foo" if we're asking for factory "bar"
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add factories
      resolver.RegisterFactory("foo.bar", new DummyFactory("foo.bar"));
      resolver.RegisterFactory("bar.foo", new DummyFactory("bar.foo"));
      resolver.RegisterFactory("foo", new DummyFactory("foo"));
      resolver.RegisterFactory("bar", new DummyFactory("bar"));

      var factory = resolver.GetFactory("test");
      Assert.AreEqual("Slf.Factories.NullLoggerFactory", factory.GetType().ToString());

    }


    [TestMethod]
    public void Resolver_Should_Properly_Deregister_Previously_Registered_Factory()
    {
      //make sure we don't return a named factory that just doesn't match
      //e.g. returning factory "foo" if we're asking for factory "bar"
      NamedFactoryResolver_Accessor resolver = new NamedFactoryResolver_Accessor();

      // check initial state
      Assert.AreEqual(0, resolver._factoryCache.Count);

      // add factories
      resolver.RegisterFactory("foo.bar", new DummyFactory("foo.bar"));
      resolver.RegisterFactory("foo.bar.xxx", new DummyFactory("bar.foo"));
      resolver.RegisterFactory("foo", new DummyFactory("foo"));
      resolver.RegisterFactory(LoggerService.DefaultLoggerName, new DummyFactory("bar"));

      var factory = resolver.GetFactory("foo.bar.TEST") as DummyFactory;
      Assert.AreEqual("foo.bar", factory.Name);

      Assert.IsTrue(resolver.DeregisterFactory("foo.bar"));

      factory = resolver.GetFactory("foo.bar.TEST") as DummyFactory;
      Assert.AreEqual("foo", factory.Name);

      Assert.IsFalse(resolver.DeregisterFactory("foo.bar"));
    }


    [TestMethod]
    public void Registering_Factories_With_Duplicate_Names_Should_Fail()
    {
      var resolver = new NamedFactoryResolver();
      resolver.RegisterFactory("foo", new ConsoleLoggerFactory());

      try
      {
        resolver.RegisterFactory("foo", new DebugLoggerFactory());
        Assert.Fail("Could regsister same factory name twice");
      }
      catch(ArgumentException e)
      {
        StringAssert.Contains(e.Message, "foo");
      }
    }

    [TestMethod]
    public void DeregisterFactory_AlreadyRegisteredFactory_RemovesFactory()
    {
      // register two factories
      var resolver = new NamedFactoryResolver();
      var factoryOne = new ConsoleLoggerFactory();
      var factoryTwo = new ConsoleLoggerFactory();
      resolver.RegisterFactory("foo", factoryOne);
      resolver.RegisterFactory("bar", factoryTwo);

      // check that they are returned 
      Assert.AreSame(factoryOne, resolver.GetFactory("foo"));
      Assert.AreSame(factoryTwo, resolver.GetFactory("bar"));

      // remove one
      resolver.DeregisterFactory("foo");

      // check resolver state
      Assert.AreSame(factoryTwo, resolver.GetFactory("bar"));
      Assert.IsInstanceOfType(resolver.GetFactory("foo"), typeof(NullLoggerFactory));

      // remove non-existant factory
      resolver.DeregisterFactory("fooBar");

      // check resolver state
      Assert.AreSame(factoryTwo, resolver.GetFactory("bar"));
      Assert.IsInstanceOfType(resolver.GetFactory("foo"), typeof(NullLoggerFactory));
    }

    [TestMethod]
    public void DeregisterFactory_CaseInsensitiveAlreadyRegisteredFactory_RemovesFactory()
    {
      // register two factories
      var resolver = new NamedFactoryResolver();
      var factoryOne = new ConsoleLoggerFactory();
      var factoryTwo = new ConsoleLoggerFactory();
      resolver.RegisterFactory("foo", factoryOne);
      resolver.RegisterFactory("bar", factoryTwo);

      // check that they are returned 
      Assert.AreSame(factoryOne, resolver.GetFactory("foo"));
      Assert.AreSame(factoryTwo, resolver.GetFactory("bar"));

      // remove one
      resolver.DeregisterFactory("FOO");

      // check resolver state
      Assert.AreSame(factoryTwo, resolver.GetFactory("bar"));
      Assert.IsInstanceOfType(resolver.GetFactory("foo"), typeof(NullLoggerFactory));
    }

    [TestMethod]
    public void Registering_Factories_With_Duplicate_Names_Case_Insensitive_Should_Fail()
    {
      var resolver = new NamedFactoryResolver();
      resolver.RegisterFactory("foo", new ConsoleLoggerFactory());

      try
      {
        resolver.RegisterFactory("FOO", new DebugLoggerFactory());
        Assert.Fail("Could regsister same factory name twice");
      }
      catch (ArgumentException e)
      {
        StringAssert.Contains(e.Message, "FOO");
      }
    }


    private class DummyFactory : ILoggerFactory
    {
      public string Name { get; set; }

      public DummyFactory()
      {
        
      }

      public DummyFactory(string name)
      {
        Name = name;
      }

      #region ILoggerFactory Members

      public ILogger  GetLogger(string name)
      {
 	      throw new NotImplementedException();
      }

      #endregion    
    }
  }
}
